package com.vividstores.integration.mq.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.apache.commons.io.FileUtils;

import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.vividstores.integration.mq.MQFile;
import com.vividstores.integration.mq.MQFileBundle;
import com.vividstores.integration.mq.MQFileReceiver;
import com.vividstores.integration.mq.MQFileReceiverHandler;

public class MQFileReceiverImpl extends AbstractMQFileDispatcher implements
		MQFileReceiver {

	private MQQueueManager queueManager;
	private MQQueue inputQueue;

	@Override
	public MQFileBundle receive(MQFileReceiverHandler handler)
			throws IOException, MQException {
		initializeQueue();

		MQMessage message = new MQMessage();

		MQGetMessageOptions gmo = new MQGetMessageOptions();
		gmo.options = MQC.MQGMO_WAIT + MQC.MQGMO_FAIL_IF_QUIESCING
				+ MQC.MQGMO_LOGICAL_ORDER + MQC.MQGMO_SYNCPOINT
				+ MQC.MQGMO_ALL_MSGS_AVAILABLE
				+ MQC.MQGMO_ALL_SEGMENTS_AVAILABLE;
		gmo.matchOptions = MQC.MQMO_MATCH_GROUP_ID;
		gmo.waitInterval = MQC.MQEI_UNLIMITED;
		
		MQFileBundle fileBundle = null;
		try {
			try {
				inputQueue.get(message, gmo);
			} catch (MQException e) {
				logger.error("Failed to get control message from queue.", e);
				throw e;
			}
			
			byte[] fileInfoBytes = null;
			try {
				fileInfoBytes = new byte[message.getMessageLength()];
				message.readFully(fileInfoBytes);
			} catch (IOException e) {
				logger.error("Failed to read data from control message.", e);
				throw e;
			}
			
			try {
				fileBundle = unmarshall(fileInfoBytes);	
			} catch (JAXBException e) {
				logger.error("Failed to unmarshal file info", e);
				throw e;
			}
			
			List<MQFile> files = fileBundle.getFiles();
			for (int i = 0; i < files.size(); i++) {
				MQFile mqFile = files.get(i);
				File destinationFile = handler.getDestinationFile(fileBundle, mqFile);
				FileOutputStream fos = FileUtils.openOutputStream(destinationFile);
				mqFile.setFile(destinationFile);
				
				try {
					while (true) {
						message.clearMessage();
						gmo.waitInterval = MQC.MQEI_UNLIMITED;

						inputQueue.get(message, gmo);

						int messageLength = message.getMessageLength();
						byte[] fileContent = new byte[messageLength];
						message.readFully(fileContent);
						fos.write(fileContent, 0, messageLength);
						if ((message.messageFlags & MQC.MQMF_LAST_SEGMENT) != 0) {
							break;
						}
					}
				} finally {
					fos.close();
				}
			}
			
			queueManager.commit();
			
			return fileBundle;
		} catch (JAXBException e) {
			queueManager.backout();
			throw new RuntimeException(
					"Failed to unmarshal using JAXB, please check JAXB environment and model mapping.",
					e);
		} catch (IOException e) {
			queueManager.backout();
			throw e;
		} catch (MQException e) {
			queueManager.backout();
			throw e;
		} finally {
			if (inputQueue != null) {
				inputQueue.close();	
			}
			queueManager.disconnect();
			queueManager.close();
		}
	}

	@SuppressWarnings("unchecked")
	private void initializeQueue() throws MQException {
		MQEnvironment.hostname = hostname;
		MQEnvironment.port = port;
		MQEnvironment.channel = channel;
		MQEnvironment.CCSID = ccsid;
		MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
				MQC.TRANSPORT_MQSERIES);
		
		queueManager = new MQQueueManager(queueManagerName);
		inputQueue = queueManager.accessQueue(queueName,
				MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING);
	}

}
